<?php

/**
 * 基类
 */
abstract class base
{

    public $time;  // 当前时间戳
    public $microtime; // 当前微秒
    public $config;  // 基本配置
    public $view;  // 模板控制类
    public $db;  // mysql连接资源
    public $redis;  // redis连接资源
    public $projectId;  // 应用ID
    public $version;  // 客户端版本号
    public $vn;  // 客户端数字版本号
    public $versionCode;//客户端数字版本号（版本检测）
    public $client;  // 客户端设备标识
    public $cid;  // 客户端设备在服务器端的编号
    public $uid; //客户端 登陆用户id
    public $vid; //客户端 游客用户id
    public $token; //绑定的token信息
    public $userInfo = array();  // 用户基本信息
    public $result = array();  // 接口数据
    public $printResult;  // 是否输出接口数据
    public $realUser;  // 虎扑用户信息
    public $fromV1; //客户端是否升级自1.0.0
    public $resStartTime; //接口开始响应速度
    public $logDb;  // 日志mysql连接资源
    public $erpDb;  // erpmysql连接资源
    public $erpmanageDb;  // erpmanageDbmysql连接资源
    public $alphaId; // 数字ID转换
    public $initType; //初始化类型
    public $isLoadPaomeBaseModel;

    public $im;//消息实体 (融云)

    /**
     * 项目列表
     * @var array
     */
    static public $projects = array(
        1 => 'hupu_Run_Android',
        2 => 'hupu_Run_IPhone',
        3 => 'hupu_Run_iOS_Pro',
    );

    public function __construct($type = 'api')
    {
        $this->initType = $type;
        $this->time = time();
        $this->microtime = microtime(TRUE);
        $environment = ENVIRONMENT;
        $this->config = config::$$environment;

        $this->projectId = common::request('t', 'G');
        !in_array($this->projectId, array_keys(self::$projects)) && $this->projectId = 1;

        if($type == 'show'){
            $this->version = '9.9.9';
            $this->vn = 9999;
        }else{
            $this->version = common::request('v', 'G');
            $this->vn = common::versionToNumber($this->version);
        }

        /*versionCode*/
        $this->versionCode = common::request('versionCode','G',0);
//        if ($this->vn >= 2887 && $this->vn <9999) {
//            if (!$this->versionCode && $this->projectId=1) {
//                $_ENV['error']->getErrors(101);
//                exit;
//            }
//        }


        $this->client = common::request('client', 'R', NULL, TRUE);
        $this->token = common::request('joggers_u', 'C', NULL, TRUE);
        !$this->token && $this->token = common::request('token', 'R', NULL, TRUE);

        $this->fromV1 = common::request('fromV1', 'G') == 1 ? TRUE: FALSE;
        $this->alphaId = $this->loadLib('alphaId', array('solt' => 'IJOGGERS*WANG*LIN*9!BCD', 'pow' => 5));

        $this->is_new = 0;
        $this->init($type);
        $uid = $this->uid;
        if($type == 'api' && !empty($uid)){
            $forbidFunc = $this->load('user')->getForbidFunction();
            if(!empty($forbidFunc)){
                $action = common::request('a','G');
                if(in_array($action,$forbidFunc)){
                    $_ENV['error']->getErrors(104);
                    exit;
                }
            }
        }
    }

    /**
     * 实例类
     * @param string $type
     */
    final public function init($type)
    {
        $this->load('error');
        switch ($type) {
            case 'admin':
                $this->in_view();
                $this->in_redis();
                $this->in_db();
                $this->in_realuser();
                $this->in_im();
                break;
            case 'default':
                $this->in_view();
                $this->in_redis();
                $this->in_db();
                $this->in_im();
                $this->in_realuser();
                break;

            case 'init':
                $this->printResult = TRUE;
                $this->in_resTime();
                $this->in_verifySign();
                $this->in_redis();
                $this->in_db();
                $this->in_im();
                $this->in_client(TRUE);
                $this->in_user(TRUE);
                break;

            case 'api':
                $this->printResult = TRUE;
                $this->in_resTime();
                $this->in_verifySign();
                $this->in_redis();
                $this->in_db();
                $this->in_im();
                $this->in_client();
                $this->in_user();
                break;
            case 'show':
                $this->in_view();
                $this->in_redis();
                $this->in_db();
                break;
            case 'outward':
                $this->printResult = TRUE;
                $this->in_resTime();
                $this->in_redis();
                $this->in_db();
                $this->in_im();
                break;
        }
    }

    /**
     * 实例模板引擎类
     */
    final public function in_view()
    {
        require FILE . 'lib/template.class.php';
        $this->view = new template();

        $this->view->assign('os', common::getOs());
        $this->view->assign('appHost', 'http://' . $this->config['host']);
    }

    final public function in_redis($rn = 'redis')
    {
        if(empty($this->$rn))
        {
            $rnMap = [
                'redis' => 'redis',
                'pubsubRedis' => 'redis_pubsub',
                'cacheRedis' => 'redis_cache',
                'mqRedis' => 'redis_mq',
            ];

            if(isset($rnMap[$rn]))
            {
                !defined('REDIS_PREFIX') && define('REDIS_PREFIX', 'HupuRun_');
                require_once FILE . 'lib/mredis.class.php';

                $crn = $rnMap[$rn];
                $this->$rn = new mredis($this->config[$crn]['ip'], $this->config[$crn]['port']);
            }
        }

        return $this->$rn;
    }

    final public function in_db($dn = 'db')
    {
        if (empty($this->$dn) || !is_object($this->$dn))
        {
            $dnMap = [
                'db' => 'mysql',
                'readDb' => 'mysql_read',
                'logDb' => 'mysql_log',
                'erpDb' => 'mysql_erp',
                'erpManageDb' => 'mysql_erp_manage',
                'totalDb' => 'mysql_total',
            ];

            if(isset($dnMap[$dn]))
            {
                require_once FILE . 'lib/db.class.php';

                $cmn = $dnMap[$dn];
                $this->$dn = new mysqldb(
                    $this->config[$cmn]['ip'], $this->config[$cmn]['user'], $this->config[$cmn]['pwd'], $this->config[$cmn]['dbname'], $this->config[$cmn]['charset']);
            }
        }

        return $this->$dn;

    }
    
    final public function in_readDb()
    {
        return $this->in_db('readDb');
    }

    /**
     * 实例数据库类
     */
    final public function in_logDb()
    {
        return $this->in_db('logDb');
    }

    /**
     * 实例数据库类
     */
    final public function in_erpDb()
    {
        return $this->in_db('erpDb');
    }

    /**
     * 初始化设备基本信息
     */
    final public function in_client($updateLastInfo = FALSE)
    {
        $this->load('client')->inClient();
        $this->load('client')->inVisitor();
        $time = $this->time;

        if($updateLastInfo)
        {
            $data['last_visit'] = $time;
            $data['version'] = $this->vn;
            $lastChannel = common::request('packageChannel','R','');
            if($this->projectId == 1 && $this->vn >=1200)
            {
                $data['pushFailCount'] = 0;
                $data['isPushVaild'] = 1;
                !empty($lastChannel) && $data['lastChannel'] = $lastChannel;
            }
            $this->cid > 0 && $this->load('client')->setClientInfo($this->cid, $data);
            $this->uid = $_ENV['client']->inUser(TRUE);
            

            
            if(in_array($this->uid,array('12537','185533','250186'))){
                //判断当前是否是初次升级到2.4版本
                $isUse = $this->redis->get(REDIS_PREFIX . "is_first_use_2400_".$this->uid);
                if(!$isUse && $this->vn == 2400){

                    $this->redis->set(REDIS_PREFIX . "is_first_use_2400_" . $this->uid,1);
                    //发送扑妞消息
                    $instrContent = $this->load('message')->getInstrMessage();
                    $this->load('message')->sendPuNiuSystemMessage($this->uid,$instrContent['首次']);
                    $this->redis->set(REDIS_PREFIX . "Instr_step_" . $this->uid, "1");
                }
            }


            //更新客户端信息
            if($this->uid)
                $this->redis->hmset(REDIS_PREFIX . 'ClientInfo_' . $this->uid, $data);
        }
        
        $this->redis->ZADD(REDIS_PREFIX.'CidVisit', $time, $this->cid);

        return ;
    }

    /**
     * 初始化 IM 融云
     * @return [type] [description]
     */
    final public function in_im(){
        if (empty($this->im) || !is_object($this->im)) {

            require FILE . 'lib/im.class.php';
            
            $this->im = new IM($this->config['im']['appKey'],$this->config['im']['appSecret']);
        }
    }

    /**
     * 获取设备绑定的用户信息
     */
    final public function in_user($init = false)
    {
        $this->load('client');
        $this->uid = $_ENV['client']->inUser();

        if ($this->uid)
        {
            if(!$init && $this->load('user')->canChangeFace($this->uid)){
                $xuid = common::request('xuid','R');
                $xuid && $this->uid = $xuid;
            }
            $this->userInfo = $_ENV['client']->getUserInfo($this->uid);

            // 补充Cookie信息
            if(empty($_COOKIE['joggers_u']))
            {
                setcookie('joggers_u', $this->token, time() + 864000 * 365 * 10, '/', '.hupu.com', FALSE, TRUE);
                setcookie('joggers_u', $this->token, time() + 864000 * 365 * 10, '/', '.ijoggers.com', FALSE, TRUE);
            }
        }
    }

    /**
     * 从COOKIE获取用户基本数据
     */
    final public function in_realuser()
    {
        require FILE . 'lib/PassportClient.class.php';

        $this->client = new PassportClient();

        if ($this->client->iflogin())
        {
            $this->realUser = $this->client->userinfo();
        }
    }

    /**
     * 载入数据层 Model
     * @param string $model Model名称
     * @param object $base  基类实例
     * @return object
     */
    final public function & load($model, $base = NULL)
    {
        $base = $base ? $base : $this;

        if (empty($_ENV[$model]))
        {
            if(stripos($model, 'paome') === 0 && !$this->isLoadPaomeBaseModel)
            {
                require FILE . "model/paome/paomeBase.class.php";
                $this->isLoadPaomeBaseModel = TRUE;
            }
            $modelFile = str_replace('.', '/', $model);
            require FILE . "model/{$modelFile}.class.php";
            $cls = str_replace('.', '', $model) . 'model';
            if (strpos($model, '/')) {
                $arrModel = explode('/', $model);
                $cls =  str_replace('.', '', end($arrModel)) . 'model';
            }
            $_ENV[$model] = new $cls($base);
        }

        return $_ENV[$model];
    }

    /**
     * 载入一个Lib库
     * @param string $libName  className
     * @param array $params
     * @return object
     */
    final public function loadLib($libName, array $params = array())
    {
        if (empty($_ENV[$libName . 'Lib']))
        {
            $fileName = FILE . "lib/{$libName}.class.php";

            if (file_exists($fileName))
            {
                require $fileName;

                $reflect = new ReflectionClass($libName);
                $_ENV[$libName . 'Lib'] = $reflect->newInstanceArgs($params);
            }
        }

        return $_ENV[$libName . 'Lib'];
    }

    /**
     * 记录响应开始时间
     */
    final public function in_resTime()
    {
        list($s1, $s2) = explode(' ', microtime());
        $this->resStartTime = sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);

        return ;
    }

    /**
     * 对比安卓或IOS版本
     * @param string $androidVersion
     * @param string $iosVersion
     * @param string $iosProVersion
     * @return boolean
     */
    public function afterVersion($androidVersion = NULL, $iosVersion = NULL, $iosProVersion = NULL)
    {
        $result = FALSE;

        //如果ios pro版本未定义，用ios版本限制默认，防止老代码逻辑错误
        ($iosVersion && !$iosProVersion) && $iosProVersion = $iosVersion;

        $androidVersion && $result = $this->projectId == 1 && common::diffVersion($this->version, $androidVersion);
        !$result && $iosVersion && $result = $this->projectId == 2 && common::diffVersion($this->version, $iosVersion);
        !$result && $iosProVersion && $result = $this->projectId == 3 && common::diffVersion($this->version, $iosProVersion);

        return $result;
    }

    /**
     * 验证签名
     */
    public function in_verifySign($error = TRUE)
    {
        $result = FALSE;
        $sign = common::request('sign', 'G');
        $packageChannel = common::request('packageChannel', 'R');
        $requestTime = common::request('time', 'R');

        if($packageChannel == 'kt')
        {
            // kt渠道特殊处理 hopes@2015-09-22

            if($sign && $requestTime && strlen($sign) == 32 && $sign == md5($this->version . $this->client . $packageChannel . $requestTime . 'kjiew8qw2daskql:dSW;2'))
            {
                $params = ['client', 't', 'v', 'time', 'frequency', 'os', 'device_id', 'resolution', 'access', 'os_version', 'device_model', 'mac_address', 'subscriberId', 'os_version_code', 'os_release'];

                $data = common::multiRequest($params, 'G');
                $data['client_type'] = $data['t'];
                $data['version'] = $data['v'];
                $data['ymd'] = date('Ymd', $data['time']);

                unset($data['t'], $data['v']);

                $this->in_db('totalDb')->insertRowTable('client_kt', $data);

                $result = TRUE;
            }
        }
        else
        {
            if($sign == $this->config['app_key']){
                //预留测试后门
                $result = true;
            }elseif($this->client && $sign)
            {
                $params = array_merge($_GET, $_POST);

                // 兼容安卓2.6.1以下版本获取天气数据接口问题
                if(!empty($_GET['c']) && !empty($_GET['a']) && $_GET['c'] == 'weather' && strtolower($_GET['a']) == 'getweather' && empty($_GET['city_id']))
                {
                    $params['city_id'] = 'null';
                }

                unset($params['c'], $params['a'], $params['v'], $params['t'],  $params['sign'], $params['header'], $params['coordinate'], $params['point_coordinate'], $params['detail_speed'], $params['northEastPoint'], $params['southWestPoint'], $params['detail_speed_curve'], $params['detail_elevation'], $params['detail_elevation_curve'], $params['detail_elevation_fold'], $params['detail_footsteps'], $params['detail_footsteps_curve'], $params['detail_footsteps_fold'], $params['detail_stride']);
                ksort($params);

                if($this->vn >= 2100)
                {
                    $serverSign = md5(urldecode(http_build_query($params, NULL, '&')) . 'SHENGZHIJIAXINYINGQUBAIFUMEI***');
                }
                else
                {
                    $serverSign = md5(urldecode(http_build_query($params, NULL, '&')) . 'HUPUWANGLINZONGJINGLI***');
                }
                // 是否显示签名
                $this->config['showSign'] == 1 && $this->result['sign'] = $serverSign;
                $serverSign == $sign && $result = TRUE;
                // $result = true;
            }
        }

        if($result == FALSE && $error == TRUE)
        {
            $_ENV['error']->getErrors(102);
            exit;
        }
        return $result;
    }
    /**
     * 检测用户昵称是否可以使用
     * @param string $nickname
     * @return int
     */
    public function checkNickname($nickname)
    {
        $result = -1;

        $len = mb_strlen($nickname, 'utf-8');

        // 判断昵称长度与格式是与合法
        if ($len >= 1 && $len <= 20 && preg_match('/^(?!_|\s\')[A-Za-z0-9\x80-\xff\s\']+$/', $nickname))
        {
            // 判断昵称是否存在违禁词
            if (!$this->load('badwords')->check($nickname) && !$this->load('badwords')->checkUserName($nickname))
            {
                // 判断昵称是否已被使用
                $uid = $this->uid;
                $checkSql = "select count(uid) from r_user where nickname='{$nickname}'";
                $checkSql = empty($uid) ? $checkSql : $checkSql." and uid != '{$uid}'";
                if (!$this->db->getOne($checkSql))
                {
                    $result = 1;
                } else
                {
                    $result = -3;
                }
            } else
            {
                $result = -2;
            }
        }

        return $result;
    }

    /**
     * 
     * @param type $inputName
     * @param type $path
     * @param int $max_size 上传文件最大大小, 单位byte
     * @return string
     */
    public function uploadFile($inputName, $path = '', $max_size = 0)
    {
        $result = FALSE;
        $dir = FILE . '../web/static/';

        if($max_size > 0 && $_FILES[$inputName]['size'] >= $max_size)
        {
            return $result;
        }
        if (!empty($_FILES[$inputName]) && $_FILES[$inputName]['error'] == 0 && in_array($_FILES[$inputName]['type'], array('image/png', 'image/gif', 'image/jpeg')))
        {
            $day = date('Ymd');
            $fix = str_replace(array('image/', 'jpeg'), array('', 'jpg'), $_FILES[$inputName]['type']);
            $upPath = $dir . $path;
            $fileName = md5($_FILES[$inputName]['name'] . microtime(TRUE)) . '.' . $fix;
            $upFileName = $upPath . $fileName;

            !file_exists($upPath) && common::mkdirs($upPath);

            if (move_uploaded_file($_FILES[$inputName]['tmp_name'], $upFileName))
            {
                $result =  $path . $fileName;
            }
        }

        return $result;
    }

    /**
     * 将游客下跑步数据转移到登陆用户下
     * @param int $uid
     * @param int $vid
     * @return string
     */
    public function turnRunData($uid, $vid)
    {
        if(empty($uid) || empty($vid))
        {
            return false;
        }
        else
        {
            $vData = $this->db->getall("select * from r_visitor_data where vid = '{$vid}' and is_delete = 0");
            if(!empty($vData))
            {
                foreach($vData as $data)
                {
                    unset($data['vid'], $data['did']);
                    $data['uid'] = $uid;
                    $this->db->insertRowTable('r_data', $data);
                }
            }
            $this->db->query("delete from r_visitor_data where vid = '{$vid}'");
        }
        return TRUE;
    }
    public function breakCall()
    {
        if(common::isInAppWebview()){
            if(!isset($this->result['status'])) $this->result['status'] = array('code' => 200, 'msg' => '正常');
            echo json_encode($this->result);
            flush();
            fastcgi_finish_request();
        }
    }
    // 处理操作不存在
    final public function __call($name, $arguments)
    {
        $this->printResult = FALSE;

        // common::dpb();

        // echo '<pre>';
        // var_dump($name, $arguments);
        // echo '</pre>';

        @header('HTTP/1.0 404 Not Found');
        exit('<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><html><head><title>404 Not Found</title></head><body><h1>404 Not Found</h1></body></html>');
    }

    public function __destruct()
    {
        if ($this->printResult && !common::request('noprint'))
        {
            header('Access-Control-Allow-Origin: http://' . (ENVIRONMENT == 'prod' ? 'irun.hupu.com' : 'irun-dev.hupu.com'));
            
            // 以JSON格式打印结果
            if(!isset($this->result['status'])) $this->result['status'] = array('code' => 200, 'msg' => '正常');
           
            echo json_encode($this->result);

            // 接口访问记录
            $this->load('log')->apiVisitRecord();
            // 接口返回空数据日志
            // $this->load('log')->emptyResponseRecord();
        }
    }
}
